package ru.slovoslovo.cropmotion.cropping;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.RasterFormatException;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.slovoslovo.cropmotion.CropSettings;

public class Cropper {
	private static final Logger LOG = LoggerFactory.getLogger(Cropper.class);

	public static final String OUTPUT_FORMAT = "JPG";

	private CropCalculator cropCalculator = null;
	private List<CropTemplate> cropTemplates = null;
	private CropSettings settings = new CropSettings();

	private int currentTempalteIndex = 0;

	public Cropper(CropSettings settings) {
		if (settings != null) {
			this.settings = settings;
		}
	}

	private String getDestPath(File file) {
		String result = "";

		switch (settings.getDestinationMode()) {
		case SAME_AS_ORIGINAL:
			result = FilenameUtils.getFullPath(file.getAbsolutePath());
			break;
		case IN_SUBFOLDER:
			result = FilenameUtils.getFullPath(file.getAbsolutePath()) + settings.getSubfolderPath()
					+ System.getProperty("file.separator");
			break;
		case SPECIFIED_PATH:
			result = settings.getDestinationPath();
			if (!result.endsWith(System.getProperty("file.separator"))) {
				result += System.getProperty("file.separator");
			}
			break;
		}

		return result;
	}

	private void arrangeTemplatesByOrder(List<CropTemplate> templates){
	    for (CropTemplate template : templates){
	        Collections.sort(template.getCropRegion(), new Comparator<CropRegion>(){
                @Override
                public int compare(CropRegion o1, CropRegion o2) {
                    int result = 0;
                    
                    if (o1.getOrder() < o2.getOrder()){
                        result = -1;
                    }else if (o1.getOrder() > o2.getOrder()){
                        result = 1;
                    } 
                    
                    return result;
                }
	        });
	    }
	    
        Collections.sort(templates, new Comparator<CropTemplate>(){
            @Override
            public int compare(CropTemplate o1, CropTemplate o2) {
                int result = 0;
                
                if (o1.getOrder() < o2.getOrder()){
                    result = -1;
                }else if (o1.getOrder() > o2.getOrder()){
                    result = 1;
                } 
                
                return result;
            }
        });
	}
	
	private List<CropTemplate> getCurrentTemplates() {
		List<CropTemplate> result = new ArrayList<CropTemplate>();

		switch (settings.getTemplateApplyMode()) {
		case ALL_FOR_EACH:
			result.addAll(getCropTemplates());
			break;
		case BY_ORDER:
			currentTempalteIndex++;
			if (currentTempalteIndex >= getCropTemplates().size()) {
				currentTempalteIndex = 0;
			}
			result.add(getCropTemplates().get(currentTempalteIndex));
			break;
		case RANDOM:
			result.add(getCropTemplates().get((new Random()).nextInt(getCropTemplates().size())));
			break;
		}

		return result;
	}

	public void cropImages(List<File> sourceFiles) {
	    int success = 0;
	    
	    arrangeTemplatesByOrder(getCropTemplates());
	    
		for (File file : sourceFiles) {
			try {
				LOG.info("Processing file " + file.getName());
				BufferedImage image = ImageIO.read(file);
				List<CropTemplate> currentTempaltes = getCurrentTemplates();

				String destDir = getDestPath(file);
				if (!new File(destDir).exists()) {
					FileUtils.forceMkdir(new File(destDir));
				}

				for (int templateIndex = 0; templateIndex < currentTempaltes.size(); templateIndex++) {
				    CropTemplate template = currentTempaltes.get(templateIndex);
					List<CropRegion> cropRegions = template.getCropRegion();
					for (int regionIndex = 0; regionIndex < cropRegions.size(); regionIndex++) {
					    CropRegion cropRegion = cropRegions.get(regionIndex);
						StringBuilder destFile = new StringBuilder();

						destFile.append(destDir).append(FilenameUtils.getBaseName(file.getAbsolutePath()));
						destFile.append('_').append(String.format("%03d", templateIndex)).append('_')
								.append(String.format("%03d", regionIndex));
						destFile.append('.').append(OUTPUT_FORMAT.toLowerCase());

						getCropCalculator().setDimension(new Dimension(image.getWidth(), image.getHeight()));
						Rectangle cropRect = getCropCalculator().getCropRectangle(cropRegion);
						BufferedImage croppedimage = null;
						try {
							croppedimage = image.getSubimage((int) cropRect.getX(), (int) cropRect.getY(),
									(int) cropRect.getWidth(), (int) cropRect.getHeight());
						} catch (RasterFormatException e) {
							LOG.error("Error cropping image by template: " + template.getOrder() + " '" + template.getTitle() + "', cropRegion: " + cropRegion.getOrder(), e);
						}
						if (croppedimage != null) {
							ImageIO.write(croppedimage, OUTPUT_FORMAT, new File(destFile.toString()));
						}
					}
				}
				success++;
			} catch (IOException e) {
				LOG.error("Error processing file " + file.getName() + "; ", e.getMessage());
			}
		}
		LOG.info("Done. " + success + " of " + sourceFiles.size() + " files successfully processed.");
	}

	public List<CropTemplate> getCropTemplates() {
		if (cropTemplates == null) {
			cropTemplates = new ArrayList<CropTemplate>();
		}
		return cropTemplates;
	}

	public void setCropTemplates(List<CropTemplate> cropTemplates) {
		this.cropTemplates = cropTemplates;
	}

	public CropCalculator getCropCalculator() {
		if (cropCalculator == null) {
			cropCalculator = new CropCalculator(new Dimension());
		}
		return cropCalculator;
	}

	public void setCropCalculator(CropCalculator cropCalculator) {
		this.cropCalculator = cropCalculator;
	}

}
